#include <prism/backend/generation/llvm/llvm-main.hpp>
//=====================================================================
#include <atma/assert.hpp>
#include <atma/string_builder.hpp>
//=====================================================================
#include <prism/frontend/syntactical_analysis/marshall.hpp>
#include <prism/frontend/syntactical_analysis/parse.hpp>
#include <prism/frontend/semantic_analysis/resolve/operator.hpp>
//=====================================================================
using namespace prism;
//=====================================================================



//=====================================================================
//
//
//
//=====================================================================
std::string llvm::lvalue_name(sooty::const_parseme_ptr_ref N)
{
	ATMA_ASSERT(N);
	std::string result;
	
	switch (N->id)
	{
		case ID::identifier:
			result = lvalue_name( marshall::identifier::variable_definition(N) );
			break;
		
		case ID::variable_definition:
			result = atma::string_builder("%")( marshall::variable_definition::name(N)->value.string );
			break;
			
		case ID::address_of:
			result = lvalue_name(N->children[0]);
			break;
			
		case ID::dereference:
			result = atma::string_builder(rvalue_name(N->children[0]))(".D");
			break;
		
		case ID::add:
		case ID::sub:
		case ID::mul:
		case ID::div:
		//case ID::dereference:
		case ID::new_:
		case ID::function_call:
			return rvalue_name(N);
	}
	
	return result;
}

namespace
{
	std::string float_to_hex_string(float f)
	{
		union {
			double f;
			unsigned char c[sizeof(double)];
		} v;
		v.f = f;

		static char buffer[18] = "0x\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

		std::fill(buffer + 2, buffer + 16, '\0');

		int written = 2;
		for ( int i = sizeof(v.c) - 1; i >= 0; --i ) {
			written += sprintf(buffer + written, "%02x", static_cast<int>(v.c[i]));
		}

		return std::string(buffer, buffer + 18);
	}
}

//=====================================================================
//
//
//
//=====================================================================
std::string llvm::rvalue_name(sooty::const_parseme_ptr_ref N)
{
	ATMA_ASSERT(N);
	
	switch (N->id)
	{
		case ID::int_literal:
			return boost::lexical_cast<std::string>(N->value.integer);
		
		case ID::bool_literal:
			return (N->value.string == "true") ? "1" : "0";
		
		// real values must be put into hexadecimal notation
		case ID::real_literal:
			return float_to_hex_string(N->value.real);
			
		case ID::identifier:
		{
			sooty::const_parseme_ptr_ref VD = marshall::identifier::variable_definition(N);
			return atma::string_builder("%")(N->value.string)(N->value.integer);
		}
		
		case ID::variable_definition:
		{
			return atma::string_builder("%")( marshall::variable_definition::name(N)->value.string );
		}
		
		case ID::add:
		case ID::sub:
		case ID::mul:
		case ID::div:
		case ID::dereference:
		case ID::new_:
		case ID::function_call:
		case ID::equ:
			return atma::string_builder("%tmp")(N->semantics.back().value.integer);
	
		case ID::address_of:
		{
			ATMA_ASSERT(N->children[0]->id == ID::identifier); // only identifiers atm
			return atma::string_builder("%")(N->children[0]->value.string);
		}
	}
	
	ATMA_HALT("bad~~~!");
	return std::string();
}





//=====================================================================
//
//
//
//=====================================================================
std::string llvm::logical_type_name(sooty::const_parseme_ptr_ref N)
{
	switch (N->id)
	{
		case ID::int_literal:
		case ID::real_literal:
		case ID::bool_literal:
			return logical_type_name(N->children[0]);
		
		case ID::int_type:
			return "i32";
			
		case ID::real_type:
			return "float";
		
		case ID::bool_type:
			return "i1";
			
		case ID::pointer_type:
			return logical_type_name(N->children[0]) + "*";

		case ID::identifier:
			return logical_type_name( marshall::identifier::variable_definition(N) );

		case ID::variable_definition:
			return logical_type_name( marshall::variable_definition::type(N) );
			
		case ID::dereference:
		{
			std::string s = logical_type_name(N->children[0]);
			s.erase(s.find_last_of('*'), 1);
			return s;
		}
		
		case ID::function_call:
		{
			return logical_type_name( resolve::type_of_pred(N)() );
		}
		
		case ID::function:
		{
			return logical_type_name( marshall::function::return_type(N) );
		}
		
		case ID::mul:
		case ID::div:
		case ID::add:
		case ID::sub:
			return logical_type_name(N->children[0]);
			
		case ID::equ:
			// hack: just taking lhs type. should figure it out.
			return logical_type_name( resolve::operator_matches(N) );
			
		case ID::eq_operator:
			return logical_type_name(N->children[0]);
		
		default:
			return std::string("logical_type_name::UNKNOWN");
	}
}



//=====================================================================
//
//
//
//=====================================================================
std::string prism::llvm::storage_type_name( sooty::const_parseme_ptr_ref N )
{
	switch (N->id)
	{
		case ID::int_type:
			return "i32*";

		case ID::real_type:
			return "float*";
		
		case ID::bool_type:
			return "i1*";
		
		case ID::identifier:
			return storage_type_name( marshall::identifier::variable_definition(N) );

		case ID::variable_definition:
			return storage_type_name( marshall::variable_definition::type(N) );
		
		case ID::pointer_type:
			return storage_type_name( N->children[0] ) + "*";
			
		case ID::dereference:
		{
			std::string s = storage_type_name(N->children[0]);
			s.erase(s.find_last_of('*'), 1);
			return s;
		}
		
		default:
			return std::string("storage_type_name::UNKNOWN");
	}
}



